package eiisan.util.model.response;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import eiisan.constant.settings.DefDesc;
import eiisan.util.spring.ContextUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Optional;

/**
 * 通用返回参数
 * @author gragonfly
 * @date 2019/6/22
 **/
@EqualsAndHashCode
@ApiModel(description = DefDesc.Message.RESULT)
@DependsOn("contextUtil")
public final class Result<T> implements Serializable {
    /**
     * 默认json缓存(数组类型),只有默认类型实例才存在
     */
    private transient byte[] cacheBytes;
    /**
     * 默认类型实例
     */
    private static transient final EnumMap<ResultCode, Result> DEFAULT_RESULT_CACHE = new EnumMap<>(ResultCode.class);
    /**
     * jackson
     */
    private static transient ObjectMapper objectMapper;

    static {
        objectMapper = ContextUtil.get(ObjectMapper.class);
        for (ResultCode item : ResultCode.values()) {
            Result result = new Result(item);
            try {
                result.cacheBytes = objectMapper.writeValueAsBytes(result);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            DEFAULT_RESULT_CACHE.put(item, result);
        }
        Assert.notNull(objectMapper, "objectMapper must not be null");
        Assert.notNull(DEFAULT_RESULT_CACHE, "DEFAULT_RESULT_CACHE must not be null");
    }
    /**
     * 状态码
     */
    @NotNull
    @ApiModelProperty(value = "状态码", example = "1000")
    private int code;
    /**
     * 获取状态
     */
    public int getCode() {
        return code;
    }

    /**
     * 提示消息
     */
    @NotNull
    @ApiModelProperty(value = "提示消息", example = "成功")
    String msg;
    /**
     * 返回消息
     */
    public String getMsg() {
        return msg;
    }

    /**
     * 承载数据
     */
    @ApiModelProperty(value = "承载数据")
    private T data;
    /**
     * 获取数据
     */
    public T getData() {
        return data;
    }



    public Result() {
    }

    private Result(IResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getDesc();
    }

    private Result(String message, IResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = message;
    }

    private Result(IResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getDesc();
        this.data = data;
    }


    /**
     * 获取json字符串(数组形式)
     */
    @SneakyThrows
    public byte[] jsonBytes() {
        return cacheBytes != null ? cacheBytes : objectMapper.writeValueAsBytes(this);
    }

    /**
     * 获取json字符串
     */
    public String json() {
        return new String(jsonBytes());
    }

    /**
     * 判断返回是否为成功<br>
     * result不为null，且code为SUCCESS.code
     */
    public static boolean isSuccess(@Nullable Result<?> result) {
        return Optional.ofNullable(result)
                .map(r -> ResultCode.SUCCESS.code == r.code)
                .orElse(Boolean.FALSE);
    }

    /**
     * 判断返回是否失败<br>
     * result为null，且code不为SUCCESS.code
     */
    public static boolean isNotSuccess(@Nullable Result<?> result) {
        return !Result.isSuccess(result);
    }

    /**
     * 返回成功结果
     */
    public static <T> Result<T> success() {
        return new Result<>(ResultCode.SUCCESS);
    }

    /**
     * 返回成功结果
     */
    public static <T> Result<T> success(T data) {
        Assert.notNull(data, "data must not be null");
        return new Result<>(ResultCode.SUCCESS, data);
    }

    /**
     * 获取对应的HttpStatus
     */
    public HttpStatus httpStatus() {
        return ResultCode.of(code).getHttpStatus();
    }

    /**
     * 获取对应的状态枚举
     */
    public ResultCode resultCode() {
        return ResultCode.of(code);
    }

    /**
     * 使用异步包装
     */
    public ListenableFuture<Result> async() {
        return new AsyncResult<>(this);
    }

    /**
     * 返回失败结果(无信息)
     */
    public static <T> Result<T> fail() {
        return new Result<>(ResultCode.FAILURE);
    }

    /**
     * 返回失败结果(有信息)
     */
    public static <T> Result<T> fail(String message) {
        Assert.notNull(message, "message must not be null");
        return new Result<>(message, ResultCode.FAILURE);
    }

    /**
     * 返回请求参数错误(无信息)
     */
    public static <T> Result<T> errorRequest() {
        return new Result<>(ResultCode.ERROR_PARAMETER);
    }

    /**
     * 返回请求参数错误(有信息)
     */
    public static <T> Result<T> errorRequest(String message) {
        Assert.notNull(message, "message must not be null");
        return new Result<>(message, ResultCode.ERROR_PARAMETER);
    }

    /**
     * 返回服务器错误结果(无信息)
     */
    public static <T> Result<T> errorServe() {
        return new Result<>(ResultCode.ERROR_SERVER);
    }

    /**
     * 返回服务器错误结果(有信息)
     */
    public static <T> Result<T> errorServe(String message) {
        return new Result<>(message, ResultCode.ERROR_SERVER);
    }

    /**
     * 返回服务器错误结果(有信息)
     */
    public static <T> Result<T> errorServe(Throwable message) {
        Assert.notNull(message, "message must not be null");
        return new Result<>(message.getMessage(), ResultCode.ERROR_SERVER);
    }

    /**
     * 返回没找到请求结果 404
     */
    public static <T> Result<T> notFound() {
        return new Result<>(ResultCode.NOT_FOUND);
    }

    /**
     * 返回未登录结果
     */
    public static <T> Result<T> notLogin() {
        return new Result<>(ResultCode.NOT_LOGIN);
    }

    /**
     * 返回已过期结果
     */
    public static <T> Result<T> expired() {
        return new Result<>(ResultCode.EXPIRED);
    }

    /**
     * 返回未通过验证结果
     */
    public static <T> Result<T> noAuth() {
        return new Result<>(ResultCode.NO_AUTH);
    }

    /**
     * 返回缺少操作权限结果
     */
    public static <T> Result<T> noAccess() {
        return new Result<>(ResultCode.NO_ACCESS);
    }

    /**
     * 返回重复请求结果
     */
    public static <T> Result<T> repeatRequest() {
        return new Result<>(ResultCode.REPEAT_REQUEST);
    }

    /**
     * 返回服务器繁忙结果
     */
    public static <T> Result<T> busyServe() {
        return new Result<>(ResultCode.BUSY_SERVER);
    }


    /**
     * 返回分页查询类型结果(仅在成功时使用)<br>
     * result.data的类型为eiisan.util.common.response.PageData
     *
     * @see PageData
     */
    public static <T> Result<PageData<T>>  page(int pageNum, int pageSize, long total, Collection<T> data) {
        Assert.notNull(data, "data must not be null");
        return new Result<>(ResultCode.SUCCESS, new PageData<>(pageNum, pageSize, total, data));
    }

    /**
     * 返回分页查询类型结果(仅在成功时使用)<br>
     * result.data的类型为eiisan.util.common.response.PageData
     *
     * @see PageData
     */
    public static <T> Result<PageData<T>>  page(Page<T> page) {
        Assert.notNull(page, "page must not be null");
        return new Result<>(ResultCode.SUCCESS, new PageData<>(page.getNumber(), page.getSize(), page.getTotalElements(),
                page.getContent()));
    }
}
